﻿using Abp;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Linq.Extensions;
using Abp.UI;
using KDS.Mes.Authorization;
using KDS.Mes.Dto;
using KDS.Mes.Mes.AlcCodes;
using KDS.Mes.Mes.Factorys;
using KDS.Mes.Mes.LineAlcCodes;
using KDS.Mes.Mes.Lines.Dtos;
using KDS.Mes.Mes.ProductModes;
using KDS.Mes.Mes.Workshops;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace KDS.Mes.Mes.Lines
{
    /// <summary>
    /// 线体信息服务实现
    /// </summary>
    [AbpAuthorize(AppPermissions.Pages_Basic_Lines)]
    public class LineAppService : MesAppServiceBase, ILineAppService
    {
        private readonly IRepository<Line, int> _lineRepository;
        private readonly ILineListExcelExporter _lineListExcelExporter;
        private readonly LineManage _lineManage;
        private readonly IRepository<Workshop, int> _workshopRepository;
        private readonly IRepository<Factory, int> _factoryRepository;
        private readonly IRepository<AlcCode, int> _alcCodeRepository;
        private readonly IRepository<LineAlcCode, int> _lineAlcCodeRepository;
        private readonly IRepository<ProductMode, int> _productModeRepository;

        /// <summary>
        /// 构造方法
        /// </summary>
        public LineAppService(
            IRepository<Line, int> lineRepository,
            LineManage lineManage,
            ILineListExcelExporter lineListExcelExporter,
            IRepository<Workshop, int> workshopRepository,
            IRepository<Factory, int> factoryRepository,
            IRepository<AlcCode, int> alcCodeRepository,
            IRepository<LineAlcCode, int> lineAlcCodeRepository,
            IRepository<ProductMode, int> productModeRepository
        )
        {
            _lineRepository = lineRepository;
            _lineManage = lineManage;
            _lineListExcelExporter = lineListExcelExporter;
            _workshopRepository = workshopRepository;
            _factoryRepository = factoryRepository;
            _alcCodeRepository = alcCodeRepository;
            _lineAlcCodeRepository = lineAlcCodeRepository;
            _productModeRepository = productModeRepository;
        }

        #region 线体信息管理
        public async Task<ListResultDto<LineListDto>> GetLines(GetLineInput input)
        {
            var lines = await _lineRepository.GetAll()
                .ToListAsync();

            return new ListResultDto<LineListDto>(lines.MapTo<List<LineListDto>>());
        }
        public PagedResultDto<LineListDto> GetPagedLines(GetLineInput input)
        {
            //初步过滤
            var query = from ln in _lineRepository.GetAll()
                        join ws in _workshopRepository.GetAll() on ln.WorkshopId equals ws.Id
                        join fc in _factoryRepository.GetAll() on ws.FactoryId equals fc.Id
                        select new
                        {
                            ln,
                            FactoryName = fc.FactoryName
                        };
            //排序
            query = !string.IsNullOrEmpty(input.Sorting) ? query.OrderBy(x => x.ln.Id) : query.OrderByDescending(t => t.ln.CreationTime);
            //获取总数
            var tasksCount = query.Count();
            //默认的分页方式
            //ABP提供了扩展方法PageBy分页方式
            var taskList = query.PageBy(input).ToList();

            return new PagedResultDto<LineListDto>(
              tasksCount,
              taskList.Select(item =>
              {
                  var dto = item.ln.MapTo<LineListDto>();
                  dto.FactoryName = item.FactoryName;
                  return dto;
              }).ToList());
        }

        /// <summary>
        /// 根据WorkshopId获取对应的Line
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public List<ComboboxItemDto> GetLineByWorkshopId(NullableIdDto<int> input)
        {
            List<ComboboxItemDto> output = new List<ComboboxItemDto>();
            var query = _lineRepository.GetAll()
                .Where(u => u.WorkshopId == input.Id).ToList();
            //query = query
            output = query
               .Select(c => new ComboboxItemDto(c.Id.ToString(), c.LineName))
               .ToList();
            return output;
        }



        /// <summary>
        /// 根据查询条件获取线体信息分页列表
        /// </summary>
        public async Task<PagedResultDto<LineListDto>> GetPagedLinesAsync(GetLineInput input)
        {
            var query = _lineRepository.GetAll();
            //TODO:根据传入的参数添加过滤条件

            var lineCount = await query.CountAsync();

            var lines = await query
            .OrderBy(input.Sorting)
            .PageBy(input)
            .ToListAsync();

            var lineListDtos = lines.MapTo<List<LineListDto>>();
            return new PagedResultDto<LineListDto>(
            lineCount,
            lineListDtos
            );
        }

        /// <summary>
        /// 通过Id获取线体信息信息进行编辑或修改
        /// </summary>
        public async Task<GetLineForEditOutput> GetLineForEditAsync(NullableIdDto<int> input)
        {
            //获取所有的ALCCode
            var lineAlcCodeDtos = (await _alcCodeRepository.GetAll()
                .OrderBy(r => r.ALCCode)
                .Select(r => new LineAlcCodeDto
                {
                    AlcCodeId = r.Id,
                    AlcCode = r.ALCCode
                })
                .ToArrayAsync());

            var output = new GetLineForEditOutput
            {
                AlcCodes = lineAlcCodeDtos
            };



            //var output = new GetLineForEditOutput();
            LineEditDto lineEditDto;
            if (input.Id.HasValue)
            {
                var entity = await _lineRepository.GetAsync(input.Id.Value);
                lineEditDto = entity.MapTo<LineEditDto>();
                output.Line = lineEditDto;

                //针对每个ALCCode返回True/False 用于view页面判断是否选定
                foreach (var lineAlcCodeDto in lineAlcCodeDtos)
                {
                    lineAlcCodeDto.IsAssigned = await IsInAlcCodeAsync(entity.Id, lineAlcCodeDto.AlcCode);
                }


                var query = (from ln in _lineRepository.GetAll()
                             join ws in _workshopRepository.GetAll() on ln.WorkshopId equals ws.Id
                             where (ws.Id == output.Line.WorkshopId)
                             select new
                             {
                                 wsFaId = ws.FactoryId,
                             }).ToList();
                output.Factorys = _factoryRepository.GetAllList()
                   .Select(c => new ComboboxItemDto(c.Id.ToString(), c.FactoryName) { IsSelected = query.FirstOrDefault().wsFaId == c.Id })
                   .ToList();

                output.Workshops = _workshopRepository.GetAllList()
                    .Where(c => c.FactoryId == query.FirstOrDefault().wsFaId)
                    .Select(c => new ComboboxItemDto(c.Id.ToString(), c.WorkshopName) { IsSelected = output.Line.WorkshopId == c.Id })
                    .ToList();

                //output.ProductModes = _productModeRepository.GetAllList()
                // .Select(c => new ComboboxItemDto(c.Id.ToString(), c.ProductModeName) { IsSelected = output.Line.ProductModeId == c.Id })
                // .ToList();
            }
            else
            {
                lineEditDto = new LineEditDto();
                output.Line = lineEditDto;
                output.Factorys = _factoryRepository.GetAllList()
               .Select(c => new ComboboxItemDto(c.Id.ToString(), c.FactoryName))
               .ToList();

                output.Workshops = _workshopRepository.GetAllList()
               .Where(c => c.FactoryId == int.Parse(output.Factorys.FirstOrDefault().Value))
               .Select(c => new ComboboxItemDto(c.Id.ToString(), c.WorkshopName))
               .ToList();

                //output.ProductModes = _productModeRepository.GetAllList()
                //.Select(c => new ComboboxItemDto(c.Id.ToString(), c.ProductModeName))
                //.ToList();

            }
            output.ProductModes = _productModeRepository.GetAllList()
             .Select(c => new ComboboxItemDto(c.Id.ToString(), c.ProductModeName) { IsSelected = output.Line.ProductModeId == c.Id })
             .ToList();
            return output;
        }

        /// <summary>
        /// 通过指定id获取线体信息ListDto信息
        /// </summary>
        public async Task<LineListDto> GetLineByIdAsync(EntityDto<int> input)
        {
            var entity = await _lineRepository.GetAsync(input.Id);

            return entity.MapTo<LineListDto>();
        }

        /// <summary>
        /// 新增或更改线体信息
        /// </summary>
        public async Task CreateOrUpdateLineAsync(CreateOrUpdateLineInput input)
        {
            if (input.LineEditDto.Id.HasValue)
            {
                await UpdateLineAsync(input);
            }
            else
            {
                await CreateLineAsync(input);
            }
        }

        /// <summary>
        /// 新增线体信息
        /// </summary>
        [AbpAuthorize(AppPermissions.Pages_Basic_Lines_Create)]
        public virtual async Task<LineEditDto> CreateLineAsync(CreateOrUpdateLineInput input)
        {
            //TODO:新增前的逻辑判断，是否允许新增
            //await CheckLineIfAlreadyExists(input);
            var entity = input.LineEditDto.MapTo<Line>();

            entity = await _lineRepository.InsertAsync(entity);

            //Assign alcCodes
            entity.AlcCodes = new Collection<LineAlcCode>();
            foreach (var alcCodeName in input.AssignedAlcCodeNames)
            {
                var alcCode = await GetAlcCodeByNameAsync(alcCodeName);
                entity.AlcCodes.Add(new LineAlcCode(entity.Id, alcCode.Id));
            }

            return entity.MapTo<LineEditDto>();
        }

        /// <summary>
        /// 编辑线体信息
        /// </summary>
        [AbpAuthorize(AppPermissions.Pages_Basic_Lines_Edit)]
        public virtual async Task UpdateLineAsync(CreateOrUpdateLineInput input)
        {
            //TODO:更新前的逻辑判断，是否允许更新
            //await CheckLineIfAlreadyExists(input);
            var entity = await _lineRepository.GetAsync(input.LineEditDto.Id.Value);
            input.LineEditDto.MapTo(entity);

            await SetAlcCodes(entity, input.AssignedAlcCodeNames);

            await _lineRepository.UpdateAsync(entity);
        }

        /// <summary>
        /// 删除线体信息
        /// </summary>
        [AbpAuthorize(AppPermissions.Pages_Basic_Lines_Delete)]
        public async Task DeleteLineAsync(EntityDto<int> input)
        {
            //TODO:删除前的逻辑判断，是否允许删除
            await _lineRepository.DeleteAsync(input.Id);
        }

        /// <summary>
        /// 批量删除线体信息
        /// </summary>
        [AbpAuthorize(AppPermissions.Pages_Basic_Lines_Delete)]
        public async Task BatchDeleteLineAsync(List<int> input)
        {
            //TODO:批量删除前的逻辑判断，是否允许删除
            await _lineRepository.DeleteAsync(s => input.Contains(s.Id));
        }

        private async Task CheckLineIfAlreadyExists(LineEditDto input)
        {
            var existingLine =
                         await (from ln in _lineRepository.GetAll()
                                join ws in _workshopRepository.GetAll() on ln.WorkshopId equals ws.Id
                                join fc in _factoryRepository.GetAll() on ws.FactoryId equals fc.Id
                                where (ln.LineName == input.LineName || ln.LineCode == input.LineCode)
                                select new
                                {
                                    Id = ln.Id
                                }).ToListAsync();


            //var existingLine = await _lineRepository.GetAll()
            //    .FirstOrDefaultAsync(l => l.LineName == lineName || l.LineCode == lineCode);
            if (existingLine == null)
            {
                return;
            }
            if (input.Id != null && existingLine.FirstOrDefault().Id == input.Id)
            {
                return;
            }
            throw new UserFriendlyException(L("ThisLineAlreadyExists"));
        }


        /// <summary>
        /// 添加/移除AlcCode
        /// </summary>
        /// <param name="line"></param>
        /// <param name="alcCodeNames"></param>
        /// <returns></returns>
        public virtual async Task<bool> SetAlcCodes(Line line, string[] alcCodeNames)
        {
            //Remove from removed alcCodes
            foreach (var lineAlcCode in line.AlcCodes.ToList())
            {
                var alcCode = await _alcCodeRepository.FirstOrDefaultAsync(lineAlcCode.AlcCodeId);

                if (alcCode != null && alcCodeNames.All(alcCodeName => alcCode.ALCCode != alcCodeName))
                {
                    await RemoveFromAlcCodeAsync(line, alcCode.ALCCode);
                }
            }

            //Add to added alcCodes
            foreach (var alcCodeName in alcCodeNames)
            {
                var alcCode = await GetAlcCodeByNameAsync(alcCodeName);
                if (line.AlcCodes.All(ur => ur.AlcCodeId != alcCode.Id))
                {
                    await AddToAlcCodeAsync(line, alcCodeName);
                }
            }

            return true;
        }

        public virtual async Task RemoveFromAlcCodeAsync(Line user, string roleName)
        {
            var role = await GetAlcCodeByNameAsync(roleName);
            var userRole = await _lineAlcCodeRepository.FirstOrDefaultAsync(ur => ur.LineId == user.Id && ur.AlcCodeId == role.Id);
            if (userRole == null)
            {
                return;
            }

            await _lineAlcCodeRepository.DeleteAsync(userRole);
        }
        private async Task<AlcCode> GetAlcCodeByNameAsync(string alcCodeName)
        {
            var alcCode = await _alcCodeRepository.FirstOrDefaultAsync(r => r.ALCCode == alcCodeName);
            if (alcCode == null)
            {
                throw new AbpException("Could not find a role with name: " + alcCodeName);
            }
            return alcCode;
        }
        public virtual async Task AddToAlcCodeAsync(Line line, string alcCodeName)
        {
            var alcCode = await GetAlcCodeByNameAsync(alcCodeName);
            await _lineAlcCodeRepository.InsertAsync(new LineAlcCode(line.Id, alcCode.Id));
        }

        public virtual async Task<bool> IsInAlcCodeAsync(int id, string alcCodeName)
        {
            var role = await GetAlcCodeByNameAsync(alcCodeName);
            return await _lineAlcCodeRepository.FirstOrDefaultAsync(ur => ur.LineId == id && ur.AlcCodeId == role.Id) != null;
        }

        #endregion 线体信息管理

        #region 线体信息的Excel导出功能

        public async Task<FileDto> GetLineToExcel()
        {
            var entities = await _lineRepository.GetAll().ToListAsync();

            var dtos = entities.MapTo<List<LineListDto>>();

            var fileDto = _lineListExcelExporter.ExportLineToFile(dtos);

            return fileDto;
        }

        #endregion 线体信息的Excel导出功能
    }
}